package net.gdface.sdk.fse;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import net.gdface.utils.Assert;
import net.gdface.utils.Judge;
import net.gdface.utils.SampleLog;
import net.gdface.utils.TimeCostProbe;

/**
 * 特征码内存搜索引擎(FSE,feature search engine)java接口类(JNI)<br>
 * 与{@link FseJniBridge}拆分开主要是为了便于生成c header文件<br>
 * @author guyadong
 *
 */
public abstract class BaseFeatureSearchEngine extends FseJniBridge {
	protected static final String ENV_FSE = "FSE_HOME";
	protected static final String PROP_FSE = ENV_FSE.toLowerCase();
	protected static final String PROP_LIBNAME = "fse_libname";
	protected static final String FSE_LIBNAME = getFseLibname();

	protected BaseFeatureSearchEngine(){
	}

	/**
	 * 加载名为{@code libname}的动态库,
	 * 如果{@code libdir}为{@code null}则调用{@link System#loadLibrary(String)}在系统搜索路径下查找动态库,
	 * 否则调用{@link System#load(String)}加载{@code libdir}指定路径下的动态库
	 * @param libdir
	 * @param libname
	 */
	protected static void loadLibrary(String libdir, String libname) {
		Assert.notEmpty(libname, "libname");
		if(null == libdir){
			SampleLog.log("load dynamic library: {}", libname);
			System.loadLibrary(libname);
		}else{
			String filename = new File(libdir,System.mapLibraryName(libname)).getAbsolutePath();
			SampleLog.log("load dynamic library: {}", filename);
			System.load(filename);
		}
	}

	/**
	 * 查找 fse_home变量<br>
	 * 优先在系统变量定义中查找'fse_home',如果找不到,再在环境变量中查找'FSE_HOME'<br>
	 * 如果都找不到则返回{@code null}
	 * @return
	 */
	protected static final String getFseHome() {
		String fseHome = System.getProperty(PROP_FSE);
		if(null==fseHome){
			if(null == (fseHome=System.getenv(ENV_FSE))){
				SampleLog.log("UNDEFINED {},"
						+ "you can defined the variable by java -D{}=<value> OR set environment variable '{}'",
						PROP_FSE,PROP_FSE,ENV_FSE);				
			}else{
				SampleLog.log("environment variable {}={}",ENV_FSE, fseHome);
			}
		}else{
			SampleLog.log("{}={},defined by java -D<name>=<value>",PROP_FSE, fseHome);
		}
		return fseHome;
	}
	protected static final String getFseLibname() {
		String libname = System.getProperty(PROP_LIBNAME);
		if(null!=libname){
			SampleLog.log("{}={},defined by java -D<name>=<value>", PROP_LIBNAME,libname);
		}
		return libname;
	}
	/**
	 * 用缺省值初始化
	 */
	protected static void init(){
		init(DEFAULT_MODE,DEFAULT_INITIAL_CAPACITY,DEFAULT_LOAD_FACTOR,DEFAULT_OVERBLOCK_CAPACITY);
	}
	/**
	 * 特征码{@code code}与数据库中的特征码比对,以相似度降序返回比对结果<br>
	 * 
	 * @param code
	 * @param sim
	 * @param rows
	 * @param imgMD5Set
	 * @param timeCost 
	 * @return {@link CodeBean}数组
	 * @see #searchCode(byte[], double, int, String[])
	 */
	public static final CodeBean[] searchCode(byte[] code, double sim, int rows, Set<String>imgMD5Set, TimeCostProbe timeCost){
		if(null!=timeCost){
			timeCost.begin();
		}
		try{
			return searchCode(code, sim, rows, imgMD5Set==null?null:imgMD5Set.toArray(new String[0]));
		}finally{
			if(null!=timeCost){
				timeCost.end();
			}
		}
	}

	/**
	 * 参见 {@link #searchCode(byte[], double, int, Set, TimeCostProbe)}
	 * @param code
	 * @param sim
	 * @param rows
	 * @param imgMD5Set
	 * @param timeCost
	 * @return
	 * @see #searchCode(byte[], double, int, Set, TimeCostProbe)
	 */
	public static final CodeBean[] searchCode(byte[] code, double sim, int rows, Collection<String>imgMD5Set, TimeCostProbe timeCost){
		return searchCode(code, sim, rows, imgMD5Set==null?null:new HashSet<String>(imgMD5Set), timeCost);		
	}
	
	/**
	 * @return 动态库已经正确加载则返回false,否则返回false
	 */
	public static boolean isLibraryLoaded() {
		return libraryLoaded;
	}

	/**
	 * 实现{@link FeatureSe}
	 * @author guyadong
	 *
	 */
	public  static class FeatureSeImpl implements FeatureSe{
		private final String name;
		/**
		 * @param name 封装实现{@link FseJniBridge}的类名，用于区分实例
		 */
		public FeatureSeImpl(String name) {
			super();
			this.name = name;
		}

		@Override
		public CodeBean[] searchCode(byte[] code, double sim, int rows) {
			return BaseFeatureSearchEngine.searchCode(code, sim, rows, null);
		}

		@Override
		public CodeBean getFeature(byte[] featureId) {
			return Judge.isEmpty(featureId) ? null : BaseFeatureSearchEngine.getFeature(featureId);
		}

		@Override
		public boolean addFeature(byte[] featureId, byte[] feature, String imgMD5) {
			return Judge.isEmpty(feature) ? false : BaseFeatureSearchEngine.addFeature(featureId, feature, imgMD5);
		}
		@Override
		public boolean addFeature(byte[] featureId, byte[] feature, long appid) {
			return addFeature(featureId, feature, CodeBean.asImgMD5(appid));
		}
		@Override
		public boolean removeFeature(byte[] featureId) {
			return Judge.isEmpty(featureId) ? false : BaseFeatureSearchEngine.removeFeature(featureId);
		}
		
		@Override
		public int size() {
			return BaseFeatureSearchEngine.size();
		}
		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			builder.append("BaseFeatureSe [jniBridge=").append(name).append("]");
			return builder.toString();
		}
	};
		
}
